// @ts-check
/**
 * 金融小数数字格式化
 * @param {String|number} s - 需要格式化得值
 * @param {number} n - 保留几位小数，默认2位
 */
 const number = (s, n = 2) => {
  // n = n > 0 && n <= 20 ? n : 2;
  s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(n) + "";
  let l = s.split(".")[0].split("").reverse();
  let r = s.split(".")[1];
  let t = "";
  for (let i = 0; i < l.length; i++) {
    t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? "," : "");
  }
  return t.split("").reverse().join("") + "." + r;
}

/**
 * 数字保留n位小数
 * @param {Number|String} value - 处理的值
 * @param {Number} n - 保留位数
 * @return {String} 保留n位小数
 */
const fixde = (value, n) => {
  let num = Number(value)
  if (num !== NaN) {
    return num.toFixed(n)
  } else {
    return '0'
  }
}

/**
 * 字符串变星号 默认隐藏中间四位 显示前3后4
 * @param {Number|String} value - 处理的值
 * @param {Number} frontLen - 前面需要保留几位
 * @param {Number} endLen - 后面需要保留几位
 * @param {String} star - 隐藏的符号，默认*
 * @return {String} 隐藏数字，字符中的部分为星号
 */
const hidden = (value, frontLen = 3, endLen = 4, star = '*') => {
  if (!value) return ''
  let str = String(value)
  let len = str.length - frontLen - endLen;
  let xing = '';
  for (let i = 0; i < len; i++) {
    xing += star;
  }
  return str.substring(0, frontLen) + xing + str.substring(str.length - endLen);
}

/**
 * 数字加百分号
 * @param {Number|String} data - 处理的值
 * @return {String} 尾部加%的字符串
 */
const percent = (data) => {
  if (data[0] == '.') {
    return '0' + data + '%'
  } else {
    return data + '%'
  }
}

/**
 * 验证电子邮箱格式
 * @param {String} value - 处理的值
 * @return {Boolean} 是否为邮箱
 */
const email = (value) => {
  return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value);
}

/**
 * 验证手机格式
 * @param {Number|String} value - 处理的值
 * @return {Boolean} 是否为手机
 */
const mobile = (value) => {
  let str = String(value)
  return /^1[23456789]\d{9}$/.test(str)
}

/**
 * 验证身份证号码
 * @param {Number|String} value - 处理的值
 * @return {Boolean} 是否为身份证号码
 */
const idCard = (value) => {
  let str = String(value)
  return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(str)
}

/**
 * 金额,只允许2位小数
 * @param {Number|String} value - 处理的值
 * @return {Boolean} 是否为只有2位小数的值
 */
const amount = (value) => {
  let str = String(value)
  //金额，只允许保留两位小数
  return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(str);
}

/**
 * 只能是字母或者数字
 * @param {Number|String} value - 处理的值
 * @return {Boolean} 是否为字母或者数字
 */
const enOrNum = (value) => {
  let str = String(value)
  return /^[0-9a-zA-Z]*$/g.test(str);
}

/**
 * 只能是数字或小数
 * @param {Number|String} value - 处理的值
 * @return {Boolean} 是否为数字或小数
 */
const floatOrNum = (value) => {
  let str = String(value)
  return /^\d+(\.\d+)?$/.test(str);
}

/**
 * 验证一个值范围
 * @param {Number|String} value - 处理的值
 * @param {Number[]|String[]} param - 范围数组 [min, max]
 * @return {Boolean} 值的大小是否在范围内
 * 
 * @example
 * range(10, [0, 20])
 */
const range = (value, param) => {
  return value >= param[0] && value <= param[1]
}

/**
 * 验证一个长度范围
 * @param {Number|String} value - 处理的值
 * @param {Number[]|String[]} param - 范围数组 [min, max]
 * @return {Boolean} 值的长度是否在范围内
 * 
 * @example
 * rangeLength(111, [0, 5])
 */
const rangeLength = (value, param) => {
  let str = String(value)
  return str.length >= param[0] && str.length <= param[1]
}

/**
 * 去除空格
 * @param {String} str - 处理的值
 * @param {String} pos - 默认both：去除两端空格；left：左侧空格；right：右侧空格；all：全部空格；
 * @return {String} 去除空格后的字符串
 */
const trim = (str, pos = 'both') => {
  if (pos == 'both') {
    return str.replace(/^\s+|\s+$/g, "");
  } else if (pos == "left") {
    return str.replace(/^\s*/, '');
  } else if (pos == 'right') {
    return str.replace(/(\s*$)/g, "");
  } else if (pos == 'all') {
    return str.replace(/\s+/g, "");
  } else {
    return str;
  }
}

/**
 * 防抖原理：一定时间内，只有最后一次操作，再过wait毫秒后才执行函数
 * @param {Function} func 要执行的回调函数 
 * @param {Number} wait 延时的时间
 * @param {Boolean} immediate 是否立即执行 
 * @return void
 */
let timeout = null;

const debounce = (func, wait = 500, immediate = false) => {
  // 清除定时器
  if (timeout !== null) clearTimeout(timeout);
  // 立即执行，此类情况一般用不到
  if (immediate) {
    var callNow = !timeout;
    timeout = setTimeout(function () {
      timeout = null;
    }, wait);
    if (callNow) typeof func === 'function' && func();
  } else {
    // 设置定时器，当最后一次操作后，timeout不会再被清除，所以在延时wait毫秒后执行func回调方法
    timeout = setTimeout(function () {
      typeof func === 'function' && func();
    }, wait);
  }
}

/**
 * 倒计时
 * @param {Date} startTime - 开始时间 日期或时间戳
 * @param {Date} endTime - 结束时间日期或时间戳
 * @return {String}
 * 
 * @example
 * timeDifference((new Date()).valueOf(), '2021/1/1 00:00:00')
 */
const timeDifference = (startTime, endTime) => { //可以传日期时间或时间戳
  let start = typeof (startTime) == "number" ? startTime : new Date(startTime).getTime(),
    end = typeof (endTime) == "number" ? endTime : new Date(endTime).getTime(),
    difference = end - start, //时间差的毫秒数
    days = Math.floor(difference / (24 * 3600 * 1000)), //计算出相差天数
    leave1 = difference % (24 * 3600 * 1000), //计算天数后剩余的毫秒数
    hours = Math.floor(leave1 / (3600 * 1000)), //计算相差分钟数
    leave2 = leave1 % (3600 * 1000), //计算小时数后剩余的毫秒数
    minutes = Math.floor(leave2 / (60 * 1000)), //计算相差秒数
    leave3 = leave2 % (60 * 1000), //计算分钟数后剩余的毫秒数
    seconds = Math.round(leave3 / 1000);
  return `${days}天${hours}小时${minutes}分钟${seconds}秒`
}

/**
 * 严格的对象类型检查。
 * @param {Object} obj - 传入对象
 * @return {Boolean} 仅对纯JavaScript对象返回true
 */
const isPlainObject = (obj) => {
  const _toString = Object.prototype.toString;
  return _toString.call(obj) === '[object Object]'
}

/**
 * 处理空数据，及正常数据显示
 * @param {Number|String} value - 数据
 * @param {String} before - 数据前缀
 * @param {String} after - 数据后缀
 * @returns {String} - 处理后的值
 */
const dispose = (value, before = '', after = '') => {
  return value ? before + value + after : '-'
}

export default {
  number,
  fixde,
  hidden,
  percent,
  email,
  mobile,
  idCard,
  amount,
  trim,
  range,
  enOrNum,
  floatOrNum,
  rangeLength,
  debounce,
  timeDifference,
  isPlainObject,
  dispose
}


/**
 * 重写方法console下方法，生产环境下不输出
 * @param {string[]}args - 'log', 'table', 'dir'
 */

export const log = (...args) => {
  args.forEach(item => {
    if (typeof console[item] !== "function") return

    console[item] = (function (data) {
      return process.env.NODE_ENV == 'development' ? data : function () { }
    })(console[item])
    window[item] = console[item]

  })
}

// 废弃
// export const iterator = () => {
//   /**
//    * 为自定义数据结构添加Symbol.iterator，使其能够通过for of 遍历
//    * 
//    * iterator(obj)
//    * for (let [key, value] of obj) {
//    *    console.log(key, value)
//    * }
//    */

//   window.iterator = (obj) => {
//     obj[Symbol.iterator] = function () {
//       // index用来记遍历圈数
//       let index = 0;
//       return {
//         next: () => {
//           return {
//             value: [Reflect.ownKeys(this)[index], this[Reflect.ownKeys(this)[index]]],
//             done: index++ >= Reflect.ownKeys(this).length - 1
//           }
//         }
//       }
//     }
//   }
// }

/**
 * 全局注册vue.filter 方法
 * @param {any} Vue - Vue原型对象
 * @param {any} obj - 需要处理的函数对象集
 * @return void
 */
export const filter = (Vue, obj) => {
  for (const [key, value] of Object.entries(obj)) {
    Vue.filter(key, value)
  }
}